# @Time : 2024-10-23 14:46
# @Author : Fioman 
# @Phone : 13149920693
class Person():
    pass


p = Person()
print(p)  # 默认实现就是打印这个对象的地址 <__main__.Person object at 0x0000015BDAFCC800>
print(repr(p))  # 这个也是一样的

# 大概的说几个object默认实现的魔术方法
o = object()
print(dir(o))


# region 1. __new__(cls,*args,**kwargs)
# 负责创建对象的实例,是构造对象时第一个调用的方法
class Person1:
    def __new__(cls, *args, **kwargs):
        print("Person1.__new__ 被调用")
        return super().__new__(cls)

    def say(self):
        print("Person1.say 被调用")


p = Person1()
p.say()


class Person2:
    def __new__(cls, *args, **kwargs):
        print("Person2.__new__被调用")
        return Person1()

    def say(self):
        print("Person2.say 被调用")


p = Person2()  # 这个创建的时候返回的对象居然是Person1,所以__new__方法的作用就是返回创建对象
p.say()


# endregion

# region 2. __init__(self,*args,**kwargs) 初始化操作
class Person3:
    def __new__(cls, *args, **kwargs):
        print(f"Person3.__new__被调用")
        return 5

    def __init__(self):
        print(f"Person3.__init__被调用")


p = Person3()  # 这里可以看到没有调用__init__很神奇
print(p)


# 那么__init__是什么时候调用的呢? 我们在创建Person3这个对象的时候才会调用__init__
class Person4:
    def __new__(cls, *args, **kwargs):
        print(f"Person4.__new__ 被调用")
        return super().__new__(cls)

    def __init__(self):
        print(f"Person4.__init__被调用")


p = Person4()


# endregion

# region 3. __del__(self) 析构方法,在对象呗垃圾回收时调用
class Person5:
    def __del__(self):
        print("Person5. __del__ 被调用")


p1 = Person5()
del p1  # 删除掉p1这个变量


# endregion

# region 4. __str__  print() str() format的时候调用
class Person6:
    def __str__(self):
        return "我是Person6"


person = Person6()
print(person)
print(str(person))
print(f"{person}")
print(repr(person))  # 这个 返回<__main__.Person6 object at 0x000002A211C0E5A0>


# endregion

# region 5. __repr__ : 面向开发者,提供对象的正式字符串表示,通常用于调试
# 如果没有定义__str__而定义了__repr__,那么str()和print()函数也会调用__repr__函数
# 但是一般的类都继承于object类,object类实现了__str__和__repr__方法
class Person7:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"Person : 姓名: {self.name},年龄: {self.age}"

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"


person = Person7("Fioman", 18)
print(person)
print(repr(person))

# endregion

# region 6. __eq__(self,other) 用于实现对象 == 比较运算符
# 如果没有实现,默认是比较两个对象的地址,就是id(a) == id(b)
a = 5
b = 5
print(a == b)  # True  int类重载了__eq__
print(id(a))
print(id(b))  # 两个id居然相同,编译器做了优化
a = 10
print(id(a))
print(id(b))  # 两个id不再相同
a = "123"
b = "123"  # True 字符串优化
print(a == b)
print(id(a) == id(b))


class Person8:
    def __init__(self, name, age):
        self.name = name
        self.age = age


p1 = Person8("Fioman", 28)
p2 = Person8("Fioman", 20)
print(p1 == p2)  # False 因为id(p1) != id(p2)


# 如何才能让其名字和年龄一样就认为是同一个人呢?实现__eq__方法
class Person9:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        print("Person9.__eq__ 方法被调用")
        return self.name == other.name and self.age == other.age


p1 = Person9("Fioman", 28)
p2 = Person9("Fioman", 28)
print(id(p) == id(p2))  # False
print(p1 == p2)  # True
print(p1 != p2)  # False,虽然没有实现__ne__方法,但是如果实现了__eq__方法,会先调用__eq__方法,然后取反


# endregion

# region 7. __ne__(self,other) 用于实现 != 比较操作,如果么有实现,会优先调用本类的__eq__方法
# 如果本类的__eq__方法没有实现,会优先调用父类的__ne__方法
class Person10:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        print("Person9.__eq__ 方法被调用")
        return self.name == other.name and self.age == other.age

    def __ne__(self, other):
        print("Person9.__ne__ 方法被调用")
        return self.name != other.name or self.age != other.age


p1 = Person10("Fioman", 28)
p2 = Person10("Fioman", 28)
print(p1 == p2)
print(p1 != p2)


# endregion

# region 8. __hash__(self) 返回对象的hash值,用于在哈希集合或者字典中作为键使用
# 调用时机: 1) 对象作为键或者存储在集合中会被调用  2) hash(obj)的时候会被调用
class Student:
    # 类变量今天变量
    hashCallCount = 0
    hashCallCount2 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __hash__(self):
        print("Student.__hash__被调用")
        self.hashCallCount += 1  # 假如我这样写就有问题,和外面的hashCallCount就不是同一个了,重新创建了一个
        Student.hashCallCount2 += 1
        return hash((self.name, self.age))


stu = Student("Fioman", 18)
print(hash(stu))  # 调用hash
s = set().add(stu)  # 调用hash
dt = {stu: s}
print(dt)
# 虽然类变量可以使用对象的方式访问,但是一般不要这么去做,容易出现问题.
print("hash调用次数: {}".format(Student.hashCallCount), stu.hashCallCount)
print("hash调用次数: {}".format(Student.hashCallCount2), stu.hashCallCount2)

stu = Student("Fioman", 18)
print(hash(stu))
print("hash调用次数: {}".format(Student.hashCallCount), stu.hashCallCount)
print("hash调用次数: {}".format(Student.hashCallCount2), stu.hashCallCount2)


# endregion

# region 9. __getattribute__(self,name) 访问对象属性时触发. (obj.arrt) or (getattr(obj,"attr"))
# 每次访问属性都会被触发,默认会根据属性名称从对象的__dict__中查找属性,或者查找继承自父类的属性
# 调用方法的时候也会被触发,方法也是属性
class Student2:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def study(self):
        print(f"{self.name} 正在学习")

    def __getattribute__(self, name):
        # name 属性名称
        print(f"属性访问 :{name},{type(name)}")
        return super().__getattribute__(name)


s = Student2("Fioman", 28)
s.study()
print(s.name)
print(s.age)
print(getattr(s, "name"))  # 这个时候也会调用__getattribute__函数
studyFunc = getattr(s, "study")  # 可以获取这个对象的函数属性,然后进行调用
studyFunc()


# endregion

# region 10. __setattr__(self,name,value) 对象属性赋值时触发 obj.attr = value or setattr(obj,'attr',value)
class Student3:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __setattr__(self, name, value):
        print(f"设置属性: {name} = {value}")
        super().__setattr__(name, value)


student = Student3("Fioman", 28)
student.name = "Hammer"
student.func = lambda x, y: x + y  # 可以随意的给它添加属性比较离谱

setattr(student, "score", 100)  # 离谱,可以添加属性
print(student.name, student.age, student.func, student.score)

# endregion
